`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    10:20:44 03/23/2012 
// Design Name: 
// Module Name:    InternalEndPoint 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module InternalDevice (
  input               CLK,
  input               RST,
  
  input               S_PRI_TVALID,
  output              S_PRI_TREADY,
  input   [127:0]     S_PRI_TDATA,
  input   [3:0]       S_PRI_TSTRB,
  input               S_PRI_TLAST,
  output              M_PRI_TVALID,
  input               M_PRI_TREADY,
  output  [127:0]     M_PRI_TDATA,
  output  [3:0]       M_PRI_TSTRB,
  output              M_PRI_TLAST,
  
  input               S_SEC_TVALID,
  output              S_SEC_TREADY,
  input   [127:0]     S_SEC_TDATA,
  input   [3:0]       S_SEC_TSTRB,
  input               S_SEC_TLAST,
  output              M_SEC_TVALID,
  input               M_SEC_TREADY,
  output  [127:0]     M_SEC_TDATA,
  output  [3:0]       M_SEC_TSTRB,
  output              M_SEC_TLAST,
  
  output reg [15:0]   ID = 0,
  output [2:0]        MAX_PAYLOAD_SIZE,
  output [2:0]        MAX_READ_REQUEST_SIZE
    );

  wire  [31:0]      cfg_di;
  wire  [31:0]      cfg_do;
  wire  [3:0]       cfg_byte_en;
  wire              cfg_rd_wr_done;
  wire  [9:0]       cfg_dwaddr;
  wire              cfg_rd_en;
  wire              cfg_wr_en;
  
  reg         tlp_cfg0;
  reg         tlp_cfg1;
  wire [4:0]  tlp_type          = S_PRI_TDATA[28:24];
  wire [2:0]  tlp_fmt           = S_PRI_TDATA[31:29];
  wire [3:0]  tlp_fdwbe         = S_PRI_TDATA[35:32];
  wire [7:0]  tlp_tag           = S_PRI_TDATA[47:40];
  wire [15:0] tlp_requester_id  = S_PRI_TDATA[63:48];
  wire [9:0]  tlp_reg_num       = S_PRI_TDATA[75:66];
  wire [2:0]  tlp_function_num  = S_PRI_TDATA[82:80];
  wire [4:0]  tlp_device_num    = S_PRI_TDATA[87:83];
  wire [7:0]  tlp_bus_num       = S_PRI_TDATA[95:88];
  wire [31:0] tlp_data          = S_PRI_TDATA[127:96];
  
  reg  [2:0]  rTlpFmt;
  reg  [4:0]  rTlpType;
  reg  [3:0]  rTlpFdwbe;
  reg  [7:0]  rTlpTag;
  reg  [15:0] rTlpRequesterId;
  reg  [9:0]  rTlpRegNum;
  reg  [2:0]  rTlpFunctionNum;
  reg  [4:0]  rTlpDeviceNum;
  reg  [7:0]  rTlpBusNum;
  reg  [31:0] rTlpData;

  wire          cpl_tvalid;
  wire          cpl_tready;
  wire [127:0]  cpl_tdata;
  wire [3:0]    cpl_tstrb;
  wire          cpl_tlast;
  reg [31:0]    rRdData;
  
  localparam 
    CHECK_HEADER = 0,
    ACCEPT_TLP = 1,
    CFG_REQ = 2,
    SEND_CPL_SC = 4,
    SEND_CPL_UR = 5,
    PASS_TLP = 6;

  reg [2:0] rState, sState;
  always @(posedge CLK)
  begin
    if (RST)
      rState <= CHECK_HEADER;
    else
      rState <= sState;
  end
  
  always @*
  begin
    sState <= rState;
    case (rState)
      CHECK_HEADER:
        if (S_PRI_TVALID)
        begin
          if (tlp_cfg0 || tlp_cfg1)
            sState <= ACCEPT_TLP;
          else
            sState <= PASS_TLP;
        end
      ACCEPT_TLP:
        if (tlp_cfg0 && (rTlpDeviceNum == 5'b0) && (rTlpFunctionNum == 3'b0))
          sState <= CFG_REQ;
        else
          sState <= SEND_CPL_UR;
      CFG_REQ:
        if (cfg_rd_wr_done)
          sState <= SEND_CPL_SC;
      SEND_CPL_SC:
        if (cpl_tready)
          sState <= CHECK_HEADER;
      PASS_TLP:
        if (S_PRI_TVALID && S_PRI_TREADY && S_PRI_TLAST)
          sState <= CHECK_HEADER;
      SEND_CPL_UR:
        if (cpl_tready)
          sState <= CHECK_HEADER;
    endcase
  end

  always @*
  begin
    tlp_cfg0 <= 1'b0;
    tlp_cfg1 <= 1'b0;
    casex({tlp_fmt, tlp_type})
      8'b0x000100: tlp_cfg0 <= 1'b1;
      8'b0x000101: tlp_cfg1 <= 1'b1;
    endcase
  end

  always @(posedge CLK)
  begin
    if (rState == CHECK_HEADER)
    begin
      rTlpFmt         <= tlp_fmt;
      rTlpType        <= tlp_type;
      rTlpFdwbe       <= tlp_fdwbe;
      rTlpTag         <= tlp_tag;
      rTlpRequesterId <= tlp_requester_id;
      rTlpRegNum      <= tlp_reg_num;
      rTlpFunctionNum <= tlp_function_num;
      rTlpDeviceNum   <= tlp_device_num;
      rTlpBusNum      <= tlp_bus_num;
      rTlpData        <= tlp_data;
    end
  end
  
  always @(posedge CLK)
  begin
    if ((rState == ACCEPT_TLP) && ({rTlpFmt, rTlpType} == 8'b01000100))
//      ID <= {rTlpBusNum, rTlpDeviceNum, rTlpFunctionNum};
      ID <= {rTlpBusNum, 5'b0, 3'b0};
  end
  
  always @(posedge CLK)
  begin
    if (cfg_rd_wr_done) 
      rRdData <= cfg_do;
  end

  assign cfg_dwaddr     = rTlpRegNum;
  assign cfg_di     = {rTlpData[7:0], rTlpData[15:8], rTlpData[23:16], rTlpData[31:24]};
  assign cfg_byte_en      = rTlpFdwbe;
  assign cfg_wr_en      = (rState == CFG_REQ) && rTlpFmt[1];
  assign cfg_rd_en      = (rState == CFG_REQ) && !rTlpFmt[1];

  assign cpl_tvalid         = (rState ==  SEND_CPL_SC) || (rState == SEND_CPL_UR);
  
  assign cpl_tdata[9:0]     = (rState == SEND_CPL_UR) ? 10'h0 :
                              rTlpFmt[1] ? 10'h0 : 10'h1;   //Length
  assign cpl_tdata[11:10]   = 2'b0;                    //AT bits
  assign cpl_tdata[13:12]   = 2'b0;                    //Attr bits
  assign cpl_tdata[14]      = 1'b0;                    //EP bit
  assign cpl_tdata[15]      = 1'b0;                    //TD bit
  assign cpl_tdata[16]      = 1'b0;                    //TH bit
  assign cpl_tdata[17]      = 1'b0;                    //Reserved
  assign cpl_tdata[18]      = 1'b0;                    //Attr 
  assign cpl_tdata[19]      = 1'b0;                    //Reserved
  assign cpl_tdata[22:20]   = 3'b0;                    //TC
  assign cpl_tdata[23]      = 1'b0;                    //Reserved
  assign cpl_tdata[28:24]   = 5'b01010;                //Type
  assign cpl_tdata[31:29]   = (rState == SEND_CPL_UR) ? 3'b000 :
                              rTlpFmt[1] ? 3'b000 : 3'b010; //Fmt
  assign cpl_tdata[43:32]   = 4;                       //Byte Count
  assign cpl_tdata[44]      = 1'b0;                    //BCM
  assign cpl_tdata[47:45]   = (rState == SEND_CPL_UR) ? 3'b001 : 3'b000;                    //CplStatus
  assign cpl_tdata[50:48]   = rTlpFunctionNum;
  assign cpl_tdata[55:51]   = rTlpDeviceNum;
  assign cpl_tdata[63:56]   = rTlpBusNum;
  assign cpl_tdata[70:64]   = 0;                       //Lower Address
  assign cpl_tdata[71]      = 1'b0;                    //Reserved
  assign cpl_tdata[79:72]   = rTlpTag;                 //Tag
  assign cpl_tdata[95:80]   = rTlpRequesterId;         //Requester ID
  assign cpl_tdata[103:96]  = rRdData[31:24];
  assign cpl_tdata[111:104] = rRdData[23:16];
  assign cpl_tdata[119:112] = rRdData[15:8];
  assign cpl_tdata[127:120] = rRdData[7:0];

  assign cpl_tstrb          = (rState == SEND_CPL_UR) ? 4'h7 : 
                              rTlpFmt[1] ? 4'h7 : 4'hF;
  assign cpl_tlast          = 1'b1;
  
  assign S_PRI_TREADY = (rState == ACCEPT_TLP) || ((rState == PASS_TLP) && M_SEC_TREADY);
  assign M_SEC_TVALID = S_PRI_TVALID && (rState == PASS_TLP);
  assign M_SEC_TDATA  = S_PRI_TDATA;
  assign M_SEC_TSTRB  = S_PRI_TSTRB;
  assign M_SEC_TLAST  = S_PRI_TLAST;
  
  wire [15:0] cfg_status = 0;
  wire [15:0] cfg_command;
  wire [31:0] cfg_bar0;
  wire [31:0] cfg_bar1;
  wire [31:0] cfg_bar2;
  wire [31:0] cfg_bar3;
  wire [31:0] cfg_bar4;
  wire [31:0] cfg_bar5;
  wire [31:0] cfg_rom;
  
  ConfigurationSpace #(
    .DEVICE_TYPE (4'h0),
    .HEADER_TYPE (8'h00))
  cs(
    .clk            (CLK),
    .rst            (RST),

    .di         (cfg_di         ),
    .do         (cfg_do         ),
    .byte_en    (cfg_byte_en    ),
    .rd_wr_done (cfg_rd_wr_done ),
    .dwaddr     (cfg_dwaddr     ),
    .rd_en      (cfg_rd_en      ),
    .wr_en      (cfg_wr_en      ),
    
    .Status     (cfg_status),
    .Command    (cfg_command),
    .BaseAddressRegister0(cfg_bar0),
    .BaseAddressRegister1(cfg_bar1),
    .BaseAddressRegister2(cfg_bar2),
    .BaseAddressRegister3(cfg_bar3),
    .BaseAddressRegister4(cfg_bar4),
    .BaseAddressRegister5(cfg_bar5),
    .ExpansionROMBaseAddress(cfg_rom)
  );

  axis_mux #(
    .DWIDTH     (128),
    .KWIDTH     (4))
  m_mux(
    .clk        (CLK        ),
    .rst        (RST        ),

    .s0_tvalid  (cpl_tvalid ),
    .s0_tready  (cpl_tready ),
    .s0_tdata   (cpl_tdata  ),
    .s0_tkeep   (cpl_tstrb  ),
    .s0_tlast   (cpl_tlast  ),

    .s1_tvalid  (S_SEC_TVALID  ),
    .s1_tready  (S_SEC_TREADY  ),
    .s1_tdata   (S_SEC_TDATA   ),
    .s1_tkeep   (S_SEC_TSTRB   ),
    .s1_tlast   (S_SEC_TLAST   ),

    .m_tvalid   (M_PRI_TVALID   ),
    .m_tready   (M_PRI_TREADY   ),
    .m_tdata    (M_PRI_TDATA    ),
    .m_tkeep    (M_PRI_TSTRB    ),
    .m_tlast    (M_PRI_TLAST    )
  );

  assign MAX_PAYLOAD_SIZE = 3'b000;
  assign MAX_READ_REQUEST_SIZE = 3'b010;
  

endmodule
